<?php 
/**
* @orm tableName users 
* @package DomAuth
* @author Oliver Leisalu, Inditel Meedia
*/
class DomUser extends DomArObject {
	
	/**
	* @orm char(25) unique
	* @orm validator length(3,25)
	*/
	public $username;
	/**
	* @orm password(6,2) not empty
	*/
	public $password;
	/**
	* @orm email not empty unique
	*/
	public $email;
	/**
	* @orm char(25)
	*/
	public $firstName;
	/**
	* @orm char(25)
	*/
	public $lastName;
	/**
	* @orm plugin DomArAcl
	*/
	public $rights;
	/**
	* @orm plugin DomArDateTime(now)
	*/
	public $registered;
	/**
	* @orm plugin DomArDateTime(now)
	*/
	public $lastLogin;
	/**
	* @orm plugin DomArDateTime(now)
	*/
	public $lastAction;
	/**
	* @orm bool
	*/
	public $blocked = false;
	/**
	* @orm text
	*/
	public $blockReason;
	/**
	* @orm bool
	*/
	public $rulesAccepted = false;
	/**
	* @orm bool
	*/
	public $emailConfirmed = false;
	
	const SESSION_VAR = 'loggedInUser';
	
	public function initLoaded() {
		// update lastAction time if needed.
		if( $this->loggedIn() && $this->lastAction->stamp() + 120 < strtotime('now') ) {
			$time = strtotime('now');
			$this->lastAction = $time;
			$this->setChanged('lastAction',false);
			DomSql::query('UPDATE '.$this->getDbTable().' SET lastAction=\''.date('Y-m-d H:i:s', $time).'\' WHERE id = '.$this->id.' LIMIT 1');
		}
	}
	
	public function getValidationErrorMessages() {
		return array(
			'username' => array(
				1 	=> 'Kasutajanimi ei ole sobiv!',
				2 	=> 'Kasutajanimi on juba võetud!',
				107 => 'Kasutajanimi on liiga pikk või liiga lühike!',
			),
			'email' => array(
				1 => 'Email ei ole sobiv!',
				2 => 'Email on juba võetud!',
				3 => 'Emaili sisestamine on kohustuslik!',
				4 => 'Emailid ei kattu!',
			),
			'password' => array(
				1 => 'Parool ei ole sobiv!',
				3 => 'Parooli sisestamine on kohustuslik!',
				4 => 'Paroolid ei kattu!',
				5 => 'Parool on liiga lühike!',
				6 => 'Parool peaks sisaldama numbreid, suuri ja väikeseid tähti!',
				7 => 'Vana parool ei ole õige!',
			),
		);
	}
	
	
	
	
	
	
	/**
	* Checks if user is logged in.
	* @return Bool
	*/
	public function loggedIn() {
		$SESSION = &self::getSession( get_class($this) );
		return $SESSION['id'] === $this->id;
	}
	/**
	* Logs user in.
	* @return Int 0 on success, 1 user email not confirmed, 2 user is blocked
	*/
	public function login() {
		if( $this->id == 0 )
			throw new RuntimeException('Only saved users can be logged in!');
			
		if( $this->emailConfirmed === false )
			return 1;
			
		if( $this->blocked === true )
			return 2;	
		
		$this->lastLogin = strtotime('now');
		$this->save('lastLogin');
		
		$SESSION = &self::getSession( get_class($this) );
		$SESSION['id'] = $this->id;
		
		DomUserActionLog::log( $this, 2);
		
		return 0;
	}
	/**
	* Logs user out
	*/
	public function logout() {
		$this->deleteLoginCookie();
		$SESSION = &self::getSession( get_class($this) );
		$SESSION = array();
		DomUserActionLog::log( $this, 3);
	}
	/**
	* @param String $username
	* @param String $password
	* @param String $className
	* @return Int 0 - success, 100 - user not found, 101 - password mismatch. Also returns errors from method $user->login().
	*/
	public static function loginByUsername( $username, $password ) {
		$className = get_called_class();
		
		$user = call_user_func( array($className,'query') )->where('username = \''.addslashes($username).'\'')->limit(1)->commit();
		if( !$user )
			return 100;
		
		$pass = $user->getProperty('password')->encodePassword($password);
		if( $pass != DOM_USER_GLOBAL_PASSWORD && $user->password != $pass ) {  
			
			// if used global password lets log it.
			if( $pass == DOM_USER_GLOBAL_PASSWORD && $user->password != $pass)
				DomUserActionLog::log( $user, 4);
			
			return 101;
		}
		
		return $user->login();
	}
	
	/**
	* @param String $username
	* @param String $password
	* @param String $className
	* @return Int 0 - success, 100 - user not found, 101 - password mismatch. Also returns errors from method $user->login().
	*/
	public static function loginByEmail( $email, $password ) {
		$className = get_called_class();
		$user = call_user_func( array($className,'query') )->where('email = \''.addslashes($email).'\'')->limit(1)->commit();
		if( !$user )
			return 100;
		
		$pass = $user->getProperty('password')->encodePassword($password);
		if( $pass != DOM_USER_GLOBAL_PASSWORD && $user->password != $pass ) {  
			
			// if used global password lets log it.
			if( $pass == DOM_USER_GLOBAL_PASSWORD && $user->password != $pass)
				DomUserActionLog::log( $user, 4);
			
			return 101;
		}
			
		return $user->login();
	}
	/**
	* @param String $className
	* @return DomUser
	*/
	public static function getLoggedInUser() {
		$className = get_called_class();
		$SESSION = &self::getSession( $className );
		return call_user_func( array($className, 'load'), $SESSION['id'] );
	}
	/**
	* @param String $className
	*/
	public static function &getSession( $className ) {
		return $_SESSION[self::SESSION_VAR][$className];
	}
	
	
	
	/**
	* Sends email confirmation email.
	*
	* It will not send email when user email is empty or user has unsolved validation.
	* You can use these keys to get some content replaced in your email body:
	* <ul>
	* <li>[$code] - Code that is needed to confirm user email. </li>
	* <li>[$username] - Username </li>
	* <li>[$firstName] - First name </li>
	* <li>[$lastName] - Last name </li>
	* <li>[$password] - Password. Must be added as argument. </li>
	* </ul>
	* You can use these keys to get some content replaced in subject:
	* <ul>
	* <li>[$username] - Username </li>
	* <li>[$firstName] - First name </li>
	* <li>[$lastName] - Last name </li>
	* </ul>
	*
	* @param String $subject Email title
	* @param String $body Email body
	* @param String/Array $from Email from where email is sent. You can also give array($fromEmail, $fromName).
	* @param String $password User password that will be added to email
	* @return Bool
	*/
	public function sendConfirmationEmail( $subject, $body, $from, $password = NULL ) {
		if( $this->email == '' ) return false;
		if( $this->validated( ($e = array()), false) === false ) return false;
		
		$code = mt_rand(100,999) . $this->id . mt_rand(100,999);
		$body = str_replace('[$code]', $code, $body);
		$body = str_replace('[$username]', $this->username, $body );
		$body = str_replace('[$firstName]', $this->firstName, $body );
		$body = str_replace('[$lastName]', $this->lastName, $body );
		$body = str_replace('[$password]', $password, $body );
		
		$subject = str_replace('[$username]', $this->username, $subject );
		$subject = str_replace('[$firstName]', $this->firstName, $subject );
		$subject = str_replace('[$lastName]', $this->lastName, $subject );
		
		$mail = new Mail();
		$mail->CharSet = 'utf-8';
		
		if( is_array( $from ) ) {
			$mail->From     = $from[0];
			$mail->FromName = $from[1];
		} else {
			$mail->From = $from;
		}
		$mail->AddAddress( $this->email, $this->firstName );

		$mail->IsHTML(true);
		
		$mail->Subject  =  $subject;
		$mail->Body     =  $body;
		//$mail->AltBody  =  "This is the text-only body";
		
		return $mail->Send();
		
	}
	/**
	* Accepts user confirmation email code as argument and then confirms user email. (Sests $user->emailConfirmed to true)
	*
	* Returned errors
	* <ul>
	* <li> null - user was not found</li>
	* </ul>
	* @param String $code
	* @param String $className
	* @return DomUser/Int User on success, Int if user was not found.
	*/
	public static function confirmUserEmail( $code ) {
		$className = get_called_class();
		$id = substr($code, 3, strlen($code)-6 );
		$user = call_user_func( array($className, 'load'), $id );

		if( is_object($user) === false ) return null;
		if( $user->emailConfirmed === true ) return $user;
		
		$user->emailConfirmed = true;
		if( $user->save() ) {
			DomUserActionLog::log( $user, 6 );
			return $user;
		} else
			throw new RuntimeException('Can\'t save user on confirming email: <br> '.implode('<br>', $log->getLastReport()->messages ));
		
	}
	
	/**
	* @param String $email
	* @param String $username Optional. if not added only email is used.
	* @param String $className
	* @return DomUser
	*/
	public static function getLostPasswordUser( $email, $username = NULL ) {
		$className = get_called_class();
		$where = '';
		if( $username !== NULL )
			$where .= 'username = \''.addslashes($username).'\' AND ';
		$where .= 'email = \''.addslashes($email).'\'';
		return call_user_func( array($className, 'query'))->where( $where )->limit(1)->commit();
	}
	
	/**
	* Sends lost password email.
	*
	* It will not send email when user email is empty or user has unsolved validation.
	* You can use these keys to get some content replaced in your email body:
	* <ul>
	* <li>[$username] - Username </li>
	* <li>[$firstName] - First name </li>
	* <li>[$lastName] - Last name </li>
	* <li>[$password] - New password. </li>
	* </ul>
	* You can use these keys to get some content replaced in subject:
	* <ul>
	* <li>[$username] - Username </li>
	* <li>[$firstName] - First name </li>
	* <li>[$lastName] - Last name </li>
	* </ul>
	*
	* @param String $subject Email title
	* @param String $body Email body
	* @param String/Array $from Email from where email is sent. You can also give array($fromEmail, $fromName).
	* @param String $password User password that will be added to email
	* @return Bool
	*/
	public function sendLostPasswordEmail( $subject, $body, $from ) {
		if( $this->email == '' ) return false;
		if( $this->validated( ($e = array()), false) === false ) return false;
	
		
		$letters = array('A','B','C','D','E','F','G','H','I','S','a','b','c','d','e','f','g','h','i','j','s');
		$password = $letters[mt_rand(0,count($letters)-1)] . $letters[mt_rand(0,count($letters)-1)] . $letters[mt_rand(0,count($letters)-1)] . $letters[mt_rand(0,count($letters)-1)] . mt_rand(100,999). $letters[mt_rand(0,count($letters)-1)] . $letters[mt_rand(0,count($letters)-1)] . mt_rand(10,99);
	
	
		$body = str_replace('[$username]', $this->username, $body );
		$body = str_replace('[$firstName]', $this->firstName, $body );
		$body = str_replace('[$lastName]', $this->lastName, $body );
		$body = str_replace('[$password]', $password, $body );
		
		$subject = str_replace('[$username]', $this->username, $subject );
		$subject = str_replace('[$firstName]', $this->firstName, $subject );
		$subject = str_replace('[$lastName]', $this->lastName, $subject );
		
		$mail = new Mail();

		if( is_array( $from ) ) {
			$mail->From     = $from[0];
			$mail->FromName = $from[1];
		} else {
			$mail->From = $from;
		}
		$mail->AddAddress( $this->email, $this->firstName );

		$mail->IsHTML(true);
		$mail->CharSet = 'utf-8';
		
		$mail->Subject  =  $subject;
		$mail->Body     =  $body;

		if( $mail->Send() ) {
			$this->password = $password;
			
			if($this->save()) {
				DomUserActionLog::log( $this, 5, 'sent to: '.$this->email);
				return true;
			
			} else {
				throw new RuntimeException('Can\'t save user on sending new password: <br> '.implode('<br>', $log->getLastReport()->messages ));
			}
		}
		return false;
	}
	

	// ----------------------------------------------------------------
	// COOKIE LOGIN
	// ----------------------------------------------------------------
	
	/**
	 * Returns cookie name.
	 * 
	 * @return String
	 */
	private static function getLoginCookieName() {
		return self::SESSION_VAR.'_'.get_called_class();
	}
	/**
	 * Sets login cookie. When this cookie is set user will be logged in automatically.
	 *
	 * In $value DO NOT store sensitive information like passwords!
	 * 
	 * @param Int $time How long user will be remembered
	 * @param String $value New params for cookie. Seperate different values with "|".
	 * @return Bool true if setting cookie was successful
	 */
	public function setLoginCookie( $time = 604800, $value = '' ) {
		$cookieName = call_user_func( array(get_class($this),'getLoginCookieName') );
		if( !defined('DOM_USER_COOKIE_PASSWORD') or DOM_USER_COOKIE_PASSWORD == '' ) {
			throw new RuntimeException('Constant DOM_USER_COOKIE_PASSWORD is not correctly set!');
		}
		
		$value = $this->id.'|'.md5( $value.'-'.$this->id . '-' . DOM_USER_COOKIE_PASSWORD ).'|'.$value;
		
		return (bool)setcookie( $cookieName, $value, (int)time()+$time,'/');
	}
	/**
	 * Removes login cookie
	 * @return Bool true if unseting was successful
	 */
	public function deleteLoginCookie() {
		$cookieName = call_user_func( array(get_class($this),'getLoginCookieName') );
		return (bool)setcookie($cookieName, "", time()-3600,'/' );
	}
	/**
	 * Gets cookie that was set for user. This method will not return cookie if it fails security check!
	 *  
	 * Returned array values
	 * 0 - user id
	 * 1 - mac 
	 * 2..N - user params
	 * 
	 * @return Array
	 */
	public static function getLoginCookie() {

		$cookieName = call_user_func( array(get_called_class(),'getLoginCookieName') );

		if( array_key_exists($cookieName, $_COOKIE) == false )
			return NULL;
		
		$className = get_called_class();
		$cookie = explode('|',$_COOKIE[$cookieName]);

		$c = $cookie;
		array_shift($c);
		array_shift($c);
		$values = implode('|',$c);

		if( $cookie[1] == md5( $values.'-'.$cookie[0] . '-' . DOM_USER_COOKIE_PASSWORD ) )
			return $cookie;
	
		return NULL;
	}
	/**
	 * Logs user in from cookie
	 * 
	 * @return Bool true on success, false on failure
	 */
	public static function loginUsingCookie() {
		$className = get_called_class();
		
		if( call_user_func(array($className,'getLoggedInUser')) )
			return false;
		
		$cookie = call_user_func( array($className,'getLoginCookie') );
		if( $cookie === NULL )
			return false;
		
		$user = DomAr::load( $cookie[0], $className);
		DomUserActionLog::log( $user, 7 );
		$user->login();

		return true;
	}
	
}
?>